#!/usr/bin/python3
# -*- coding: utf-8 -*-

import time
import os
import subprocess
import shlex
import datetime

# jar 定义
JARS = (
    {'alias': 'demo-80', 'name': 'demo-80.jar', 'port': 8080},
    {'alias': 'demo-81', 'name': 'demo-81.jar', 'port': 8081},
    {'alias': 'demo-82', 'name': 'demo-82.jar', 'port': 8082}
)
SIZE = len(JARS)

# JAR 包路径
JAR_PATH = '/home/may/develop/workspace/pycharm'
if not os.path.exists(JAR_PATH):
    os.makedirs(JAR_PATH)

# 日志路径
LOG_PATH = f'{JAR_PATH}/logs'
if not os.path.exists(LOG_PATH):
    os.makedirs(LOG_PATH)


# 脚本入口
def main():
    os.system('clear')
    while True:
        print("=============================================================================\n")
        print("                            微服务管理 - 2.0版")
        print("                            designed by bobby\n")
        print("                            1.服务状态")
        print("                            2.全部启动")
        print("                            3.全部停止")
        print("                            4.全部重启")
        print("                            5.服务操作")
        print("                            6.退出\n")
        print("============================================================================")
        num = common_input(6)
        if not num:
            continue
        if num == 1:
            status()
        elif num == 2:
            start_all()
        elif num == 3:
            stop_all()
        elif num == 4:
            restart_all()
        elif num == 5:
            list_all()
        else:
            exit()


def status():
    os.system('clear')
    print('=============================================================================\n')
    print('                               服务状态\n')
    print('=============================================================================')
    for jar in JARS:
        alias = jar['alias']
        name = jar['name']
        cmd = f"""ps -ef | grep -w {name} | grep -v "grep" | awk '{{print $2}}'"""
        pid = cmd_out(cmd)
        if pid == "":
            print(f'{alias} is\033[31m stopped \033[0m')
        else:
            print(f'{alias} is\033[32m running \033[0m')


# 并行启动所有
def start_all():
    os.system('clear')
    print('=============================================================================\n')
    print('                               启动所有服务\n')
    print('=============================================================================')
    # 需要启动的 jar
    need_start = []
    for jar in JARS:
        alias = jar['alias']
        name = jar['name']
        port = jar['port']
        pid_cmd = f"""lsof -i:{port} | grep "LISTEN"|awk '{{print $2}}'"""
        pid = cmd_out(pid_cmd)
        if pid == "":
            name_arr = name.split('.')[:-1]
            pure_name = ''.join(name_arr)
            start_cmd = f'nohup java -jar {JAR_PATH}/{name} > {LOG_PATH}/{pure_name}.log 2>&1 &'
            os.system(start_cmd)
            need_start.append(jar)
            print(f'{alias} is starting...')
    while len(need_start) > 0:
        time.sleep(5)
        has_started = []
        for jar in need_start:
            alias = jar['alias']
            port = jar['port']
            pid = cmd_out(f"""lsof -i:{port} | grep "LISTEN"|awk '{{print $2}}'""")
            if pid != "":
                print(f'{alias} start \033[32m successful \033[0m')
                has_started.append(jar)
        if len(has_started) > 0:
            for jar in has_started:
                need_start.remove(jar)
    print('all is running')


def start(alias, name, port):
    pid = cmd_out(f"""lsof -i:{port} | grep "LISTEN"|awk '{{print $2}}'""")
    if pid != "":
        print(f'{alias} is running')
    else:
        print(f'{alias} is starting ...')
        name_arr = name.split('.')[:-1]
        pure_name = ''.join(name_arr)
        cmd = f'nohup java -jar {JAR_PATH}/{name} > {LOG_PATH}/{pure_name}.log 2>&1 &'
        os.system(cmd)
        while True:
            time.sleep(5)
            pid = cmd_out(f"""lsof -i:{port} | grep "LISTEN"|awk '{{print $2}}'""")
            if pid != "":
                break
        print(f'{alias} start \033[32m successful \033[0m')


def stop_all():
    os.system('clear')
    print('=============================================================================\n')
    print('                               停止所有服务\n')
    print('=============================================================================')
    for jar in JARS:
        alias = jar['alias']
        name = jar['name']
        stop(alias, name)
    print('all is stopped')


def stop(alias, name):
    cmd = f"""ps -ef | grep -w {name} | grep -v "grep" | awk '{{print $2}}'"""
    pid = cmd_out(cmd)
    if pid != "":
        os.system(f'kill -9 {pid}')
        print(f'{alias} is killed')


def restart_all():
    stop_all()
    time.sleep(2)
    start_all()


def restart(alias, name, port):
    stop(alias, name)
    time.sleep(2)
    start(alias, name, port)


# 列出所有服务
def list_all():
    os.system('clear')
    while True:
        print('=============================================================================\n')
        print('                                所有服务\n')
        print('=============================================================================')
        for idx, jar in enumerate(JARS):
            print('{0}.{1}'.format(idx + 1, jar['alias']))
        print('{}.返回上一级'.format(SIZE + 1))
        print('{}.退出'.format(SIZE + 2))
        num = common_input(SIZE + 2)
        if not num:
            continue
        elif num == SIZE + 1:
            main()
        elif num == SIZE + 2:
            exit()
        else:
            jar = JARS[num - 1]
            handler(jar['alias'], jar['name'], jar['port'])


def handler(alias, name, port):
    os.system('clear')
    while True:
        print('=============================================================================\n')
        print(f'                               {alias}\n')
        print('=============================================================================')
        print('1.启动')
        print('2.停止')
        print('3.重启')
        print('4.查看日志')
        print('5.返回上一级')
        print('6.返回主目录')
        print('7.退出')
        num = common_input(7)
        if not num:
            continue
        if num == 1:
            start(alias, name, port)
        elif num == 2:
            stop(alias, name)
        elif num == 3:
            restart(alias, name, port)
        elif num == 4:
            log(name)
        elif num == 5:
            list_all()
        elif num == 6:
            main()
        else:
            exit()


def log(name, line=100):
    name_arr = name.split('.')[:-1]
    pure_name = ''.join(name_arr)
    os.system(f'tail -n {line} {LOG_PATH}/{pure_name}.log')


# 封装通用的输入方法,n 为最大操作项序号
def common_input(n):
    num = input('请输入指令序号:')
    if not num.isdigit():
        print('调皮，请按提示输入喔！')
        time.sleep(1)
        os.system('clear')
        return False
    else:
        num = int(num)
        if num < 1 or num > n:
            print('无效指令!')
            time.sleep(1)
            os.system('clear')
            return False
        else:
            return num


# 获取 shell 命令输出
def cmd_out(cmd):
    out = subprocess.check_output(cmd, shell=True)
    return out.decode()


"""
执行shell命令，封装了 subprocess 的 Popen 方法，支持超时
:param cmd: 需要执行的命令
:param cwd: 运行命令时，根路径
:param timeout: 超时时间
:param shell: 是否通过 shell 运行
:return: return_code
Raises: Exception: 执行超时
"""


def execute(cmd, cwd=None, timeout=None, shell=False):
    if shell:
        cmd_string_list = cmd
    else:
        cmd_string_list = shlex.split(cmd)  # 使用 shlex 可以按照 Linux 下的命令分割

    end_time = 0
    if timeout:
        end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)

    # 没有指定标准输出和错误输出的管道，因此会打印的屏幕上
    sub = subprocess.Popen(cmd_string_list, cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=shell,
                           bufsize=4096)
    # subprocess.poll() 方法：检查子进程是否结束，如果结束了会有返回码
    # 方在 subprocess.returncode 变量中
    while sub.poll() is None:
        time.sleep(0.1)
        if timeout:
            if end_time <= datetime.datetime.now():
                raise Exception('timeout:{}', format(cmd))
    return str(sub.returncode)


if __name__ == '__main__':
    main()
